<?php
namespace Pinenut\Model;

use Pinenut\Log;

abstract class Mongo extends \Pinenut\Model {
	
	public static $adapter = '\MongoAdapterConfig';
	public static $collection = null; //abstract!
	
	/**
	 * @var array of \MongoCollection
	 */
	protected static $connect = array();

	/**
	 * Собираем и выполняем запрос на выборку данных
	 * 
	 * @param array $where
	 * @param array $fields
	 * @return array
	 */
	static function select(array $where = null, 
							array $fields = null, array $order = null, 
							$offset = null, $limit = null) {
								
		Log::begin();
		$query = static::doCriteria($where);

		$result = static::connect()->find($query, $fields);
		if (null !== $offset) $result->skip(intval($offset));
		if (null !== $limit) $result->limit(intval($limit));
		$explain = $result->explain();
		$data = iterator_to_array($result);
		Log::end('Select', array('query'=>$query, 'fields'=>$fields, 'explain'=>$explain));
		return $data;
	}
	
	/**
	 * Возвращаем кол-во записей по условию
	 * 
	 * @param array $where
	 * @returnint
	 */
	static function count(array $where = null) {
		$query = static::doCriteria($where);
		return static::connect()->count($query);
	}
	
	
	static protected function doCriteria(array $where) {
		
		$query = array();
			
		foreach ($where as $row) {

			// типекастим
			if ($type = static::getFieldType($row['field'])) {
				if (is_array($row['value'])) {
					foreach ($row['value'] as &$v) {
						$v = static::typeCast($v, $type);
					}
				} else {
					$row['value'] = static::typeCast($row['value'], $type);
				}
			}
			
			// формируем запрос в зависимости от операции
			
			switch ($row['op']) {
				case Finder::OP_EQ:
					$query[$row['field']] = $row['value'];
					break;
				case Finder::OP_NEQ:
					$query[$row['field']]['$ne'] = $row['value'];
					break;
				case Finder::OP_IN:
					$query[$row['field']]['$in'] = $row['value'];
					break;
					
				case Finder::OP_NIN:
					$query[$row['field']]['$nin'] = $row['value'];
					break;
					
				case Finder::OP_GT:
					$query[$row['field']]['$gt'] = $row['value'];
					break;
				
				case Finder::OP_LT:
					$query[$row['field']]['$lt'] = $row['value'];
					break;
										
				case Finder::OP_GTE:
					$query[$row['field']]['$gte'] = $row['value'];
					break;
					
				case Finder::OP_LTE:
					$query[$row['field']]['$lte'] = $row['value'];
					break;
			}
		}
		
		return $query;
	} 
	
	/**
	 * Коннект к коллекции базы
	 * @return \MongoCollection
	 */
	static function connect() {
		
		if (!isset(static::$connect[static::$collection])) {
			$configClassName = static::$adapter;
			$config = $configClassName::get();
			$client = new \MongoClient(@$config['server'], @$config['options']);
			$db = $client->selectDB(@$config['db']);
			static::$connect[static::$collection] = $db->selectCollection(static::$collection);
		}
		return static::$connect[static::$collection];
	}
	
	public function save() {
		Log::begin();
		// если идентификатор не указан - значит новая запись, иначе делаем обновление
		$data = $this->toArray(true);
		if (empty($data['_id'])) 
			unset($data['_id']);
			
		if (static::connect()->save($data)) {
			// OK!
			// населяем запись для обновления значений (_id для новой записи, поля-объекты и т.д.)
			$this->populate($data, true);
			Log::end('Save', $data);
		} else {
			Log::end('Save - error', $data);
			throw new Exception('Запись не сохранена', 503);
		}

	}
	
	
	static function update(array $where = null, array $operators = null) {
		Log::begin();
		
		// подготавливаем условие	
		$query = static::doCriteria($where);
		$addToSet = $inc = array();
		
		// готовим операции
		if (isset($operators['addToSet']) && is_array($operators['addToSet'])) 
			foreach ($operators['addToSet'] as $field=>$data) {
				$addToSet[$field]['$each'] = $data;
			} 
			
			
		if (isset($operators['inc']) && is_array($operators['inc']))
			foreach ($operators['inc'] as $field=>$data) {
				$inc[$field] = $data;
			}
			
		if (isset($operators['set']) && is_array($operators['set']))
			foreach ($operators['set'] as $field=>$data) {
				$set[$field] = $data;
			}
			
			
		$op = array();
		
		if (!empty($addToSet)) $op['$addToSet'] = $addToSet;
		
		if (!empty($inc)) $op['$inc'] = $inc;
		
		if (!empty($set)) $op['$set'] = $set;

		$res = static::connect()->update($query, $op);
		
		Log::end('Update', array('query'=>$query, 'op'=>$op));
			
	}
	
	
	static function typeCast($value, $type) {
		switch ($type) {
 			case static::ID:
 				$value = new \MongoID($value);
 				break;
			case static::DATE:
				$value = null === $value?null:new \MongoDate($value);
				break;
			default:
				$value = parent::typeCast($value, $type);
		}
		
		return $value;
	}
}